package com.regan.erp.aop;

import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.*;
import cn.hutool.http.HttpUtil;
import com.google.common.collect.Lists;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.kit.StrKit;
import com.regan.erp.exception.MyBaselogicException;
import com.regan.erp.model.ResultPoJo;
import com.regan.erp.model.User;
import com.regan.erp.util.*;

import java.lang.reflect.Method;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 针对注解 拦截器
 * include：
 *      a. NotNullPara 参数为空验证
 *      b. SubLimit 重复提交限制
 * @author zhangby
 * @date 2018/2/28 上午11:39
 */
public class AnnotationParaInterceptor implements Interceptor {
    /** 手机号验证正则 */
    private static final String PHONE_REGEX = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0-9])|(17[0-9]))\\d{8}$";
    private static final String FIXEDINES_REGEX = "^(0\\d{2}-\\d{8}(-\\d{1,4})?)|(0\\d{3}-\\d{7,8}(-\\d{1,4})?)$";
    private static final String EMAIL_REGEX = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
    private static final String POSTCODE_REGEX = "^[1-9]\\d{5}(?!\\d)$";

    /** 异常页面 */
    private String exceptionView = "/html/other/error.html";

    /** 基础构造器 */
    public AnnotationParaInterceptor() {
    }
    public AnnotationParaInterceptor(String exceptionView) {
        this.exceptionView = exceptionView;
    }

    /** 拦截器实现 */
    @Override
    public void intercept(Invocation inv) {
        Controller controller = inv.getController();
        Method method = inv.getMethod();
        /** 参数非空拦截 */
        NotNullPara notNullPara = method.getAnnotation(NotNullPara.class);
        if (ObjectUtil.isNotNull(notNullPara)) {
            List<String> params = Lists.newArrayList(notNullPara.value());
            if (verifyNotNullPara(inv, controller, params)) {
                return;
            }
        }
        /** 重复提交限制 */
        SubLimit subLimit = method.getAnnotation(SubLimit.class);
        if (ObjectUtil.isNotNull(subLimit)) {
            User user = UserUtil.getUser(controller.getSession());
            //获取当前访问的ip地址
            String clientIP = HttpUtil.getClientIP(controller.getRequest());
            String methodName = inv.getMethodName();
            //将访问限制存入redis
            String redisKey = StrUtil.format(ConfigUtil.redisKey().get("userSubmit"), clientIP+"-"+methodName);
            String val = RedisUtil.get(redisKey);
            if (StrKit.notBlank(val)) {
                renderError(inv, StrFormatter.format(Constants.error_msg("107"), subLimit.second()), null);
                return;
            } else {
                RedisUtil.set(redisKey, ObjectUtil.isNotNull(user)?user.getId():methodName, subLimit.second());
            }
        }
        /** 参数验证 */
        Verify4Para verify4Para = method.getAnnotation(Verify4Para.class);
        if (ObjectUtil.isNotNull(verify4Para)) {
            try {
                List<String> errorList = Lists.newArrayList();
                //验证手机号
                verify4Data(verify4Para::phone, (p, li) -> {
                    if (ObjectUtil.isNotNull(controller.getPara(p)) && !ReUtil.isMatch(PHONE_REGEX, controller.getPara(p))) {
                        li.add(p);
                    }
                }, li -> errorList.add("请输入正确的手机号[" + ArrayUtil.join(li.toArray(), ",") + "]"));

                //验证固话
                verify4Data(verify4Para::fixedine, (p, li) -> {
                    if (ObjectUtil.isNotNull(controller.getPara(p)) && !ReUtil.isMatch(FIXEDINES_REGEX, controller.getPara(p))) {
                        li.add(p);
                    }
                }, li -> errorList.add("请输入正确的固定电话[" + ArrayUtil.join(li.toArray(), ",") + "]"));

                //邮政编码
                verify4Data(verify4Para::postCode, (p, li) -> {
                    if (ObjectUtil.isNotNull(controller.getPara(p)) && !ReUtil.isMatch(POSTCODE_REGEX, controller.getPara(p))) {
                        li.add(p);
                    }
                }, li -> errorList.add("请输入正确的邮政编码[" + ArrayUtil.join(li.toArray(), ",") + "]"));

                //验证邮箱
                verify4Data(verify4Para::email, (p, li) -> {
                    if (ObjectUtil.isNotNull(controller.getPara(p)) && !ReUtil.isMatch(EMAIL_REGEX, controller.getPara(p))) {
                        li.add(p);
                    }
                }, li -> errorList.add("请输入正确的邮箱[" + ArrayUtil.join(li.toArray(), ",") + "]"));

                //验证数值小于等于 -> title:20
                verify4Data(verify4Para::min, (m, errList) -> {
                    List<String> splits = CommonUtil.splitStr4Temp(m, "{}:{}");//数据解析格式化
                    if (ObjectUtil.isNotNull(controller.getPara(splits.get(0)))) {
                        //验证长度
                        if (Double.parseDouble(controller.getPara(splits.get(0))) < Double.parseDouble(splits.get(1))) {
                            errList.add(splits.get(0) + "值不得小于" + (Integer.parseInt(splits.get(1))));
                        }
                    }
                }, errList -> errorList.add(ArrayUtil.join(errList.toArray(), ",")));

                //验证长度小于等于
                verify4Data(verify4Para::minLength, (m, errList) -> {
                    List<String> splits = CommonUtil.splitStr4Temp(m, "{}:{}");//数据解析格式化
                    if (ObjectUtil.isNotNull(controller.getPara(splits.get(0)))) {
                        //验证长度
                        if (controller.getPara(splits.get(0)).length() < Integer.parseInt(splits.get(1))) {
                            errList.add(splits.get(0) + "长度不得小于" + Integer.parseInt(splits.get(1)));
                        }
                    }
                }, errList -> errorList.add(ArrayUtil.join(errList.toArray(), ",")));

                //验证数值长度大于
                verify4Data(verify4Para::max, (m, errList) -> {
                    List<String> splits = CommonUtil.splitStr4Temp(m, "{}:{}");//数据解析格式化
                    if (ObjectUtil.isNotNull(controller.getPara(splits.get(0)))) {
                        //验证长度
                        if (Double.parseDouble(controller.getPara(splits.get(0))) > Double.parseDouble(splits.get(1))) {
                            errList.add(splits.get(0) + "值不得大于" + (Integer.parseInt(splits.get(1))));
                        }
                    }
                }, errList -> errorList.add(ArrayUtil.join(errList.toArray(), ",")));

                //验证长度大于等于
                verify4Data(verify4Para::maxLength, (m, errList) -> {
                    List<String> splits = CommonUtil.splitStr4Temp(m, "{}:{}");//数据解析格式化
                    if (ObjectUtil.isNotNull(controller.getPara(splits.get(0)))) {
                        //验证长度
                        if (controller.getPara(splits.get(0)).length() > Integer.parseInt(splits.get(1))) {
                            errList.add(splits.get(0) + "长度不得小于" + Integer.parseInt(splits.get(1)));
                        }
                    }
                }, errList -> errorList.add(ArrayUtil.join(errList.toArray(), ",")));

//                //验证长度范围 range -> remarks:[10,30]
                verify4Data(verify4Para::range, (m, errList) -> {
                    List<String> splits = CommonUtil.splitStr4Temp(m, "{}:[{},{}]");//数据解析格式化
                    if (ObjectUtil.isNotNull(controller.getPara(splits.get(0)))) {
                        //验证长度
                        if (controller.getPara(splits.get(0)).length() < Integer.parseInt(splits.get(1)) ||
                                controller.getPara(splits.get(0)).length() > Integer.parseInt(splits.get(2)) ) {
                            errList.add(splits.get(0) + "长度不得小于" + splits.get(1)+"不得大于"+splits.get(2));
                        }
                    }
                }, errList -> errorList.add(ArrayUtil.join(errList.toArray(), ",")));

//                //验证数值范围 range -> remarks:[10,30]
                verify4Data(verify4Para::rangeNum, (m, errList) -> {
                    List<String> splits = CommonUtil.splitStr4Temp(m, "{}:[{},{}]");//数据解析格式化
                    if (ObjectUtil.isNotNull(controller.getPara(splits.get(0),"0"))) {
                        //验证长度
                        if (Double.parseDouble(controller.getPara(splits.get(0))) < Double.parseDouble(splits.get(1)) ||
                                Double.parseDouble(controller.getPara(splits.get(0))) > Double.parseDouble((splits.get(2)))) {
                            errList.add(splits.get(0) + "长度不得小于" + splits.get(1)+"不得大于"+splits.get(2));
                        }
                    }
                }, errList -> errorList.add(ArrayUtil.join(errList.toArray(), ",")));

                //number 必须为数字
                verify4Data(verify4Para::number,(m,errList)->{
                    if (ObjectUtil.isNotNull(controller.getPara(m)) && !NumberUtil.isNumber(controller.getPara(m))) {
                        errList.add(m);
                    }
                },errList->errorList.add("["+ArrayUtil.join(errList.toArray(),",")+"]必须为数字类型"));

                //bool 判断是否为布尔类型
                verify4Data(verify4Para::bool,(m,errList)->{
                    if (ObjectUtil.isNotNull(controller.getPara(m))) {
                        String val = controller.getPara(m).trim();
                        if (!"true".equals(val) && !"false".equals(val)) {
                            errList.add(m);
                        }
                    }
                },errList->errorList.add("["+ArrayUtil.join(errList.toArray(),",")+"]必须为布尔类型"));

                //idCard 判断身份证号
                verify4Data(verify4Para::idCard,(m,errList)->{
                    if (ObjectUtil.isNotNull(controller.getPara(m))) {
                        String val = controller.getPara(m).trim();
                        if (!IdcardUtil.isValidCard(val)) {
                            errList.add(m);
                        }
                    }
                },errList->errorList.add("请输入正确的身份证号["+ArrayUtil.join(errList.toArray(),",")+"]"));

                if (!errorList.isEmpty()) {
                    String errormsg = ArrayUtil.join(errorList.toArray(), ",");
                    renderError(inv, errormsg, null);
                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new MyBaselogicException("999", "数据过滤验证异常");
            }
        }
        inv.invoke();
    }

    /**
     * 验证
     * @param supplier
     */
    private void verify4Data(Supplier<String[]> supplier, BiConsumer<String,List<String>> consumer, Consumer<List<String>> errConsumer) {
        List<String> mins = filterNull(Lists.newArrayList(supplier.get()));
        if (ObjectUtil.isNotNull(mins) && !mins.isEmpty()) {
            List<String> errList = Lists.newArrayList();
            mins.forEach(m -> consumer.accept(m,errList));
            if (!errList.isEmpty()) {
                errConsumer.accept(errList);
            }
        }
    }

    /** 验证参数是否为空 */
    private boolean verifyNotNullPara(Invocation inv, Controller controller, List<String> params) {
        List<String> paramList = Lists.newArrayList();
        params.forEach(key -> {
            if (ObjectUtil.isNull(controller.getPara(key)) || StrKit.isBlank(controller.getPara(key))) {
                paramList.add(key);
            }
        });
        if (!paramList.isEmpty()){
            String errormsg = StrFormatter.format(Constants.error_msg("900"), ArrayUtil.join(paramList.toArray(), ","));
            renderError(inv,errormsg,null);
            return true;
        }
        return false;
    }

    /**
     * 过滤空值
     * @param li
     * @param <T>
     * @return
     */
    private <T> List<T> filterNull(List<T> li) {
        return li.stream().filter(t -> ObjectUtil.isNotNull(t) && StrKit.notBlank(t.toString())).collect(Collectors.toList());
    }

    /** 页面跳转 */
    private void renderError(Invocation inv, String errorMsg, String errorRedirect) {
        if (StrKit.notBlank(errorRedirect)) {
            inv.getController().redirect(errorRedirect);
            return;
        }
        //返回错误信息
        if (inv.getMethodName().indexOf("to") > -1) {
            //页面统一跳转错误页面
            inv.getController().setAttr("error", errorMsg);
            inv.getController().render(StrKit.notBlank(errorRedirect)?errorRedirect:exceptionView);
        }else{
            ResultPoJo msg = ResultPoJo.create()
                    .code(Constants.ERROR)
                    .msg(errorMsg);
            inv.getController().renderJson(msg);
        }
    }
}
